/*
 * Copyright (C) 2013 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY SONY COMPUTER ENTERTAINMENT INC. ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL SONY COMPUTER ENTERTAINMENT INC.
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebMemoryCacheInfo.h"

#include "MemoryCache.h"

namespace {

JSObjectRef s_object;
WebCore::MemoryCache::Statistics s_statistics;

// Define wrapper functions to get member variables of MemoryCache::Statistics.
typedef const WebCore::MemoryCache::TypeStatistic& (*StatisticGetter)(void);
#define DEFINE_STATISTIC_GETTER(propName) \
    const WebCore::MemoryCache::TypeStatistic& propName() { return s_statistics.propName; }

DEFINE_STATISTIC_GETTER(images)
DEFINE_STATISTIC_GETTER(cssStyleSheets)
DEFINE_STATISTIC_GETTER(scripts)
DEFINE_STATISTIC_GETTER(xslStyleSheets)
DEFINE_STATISTIC_GETTER(fonts)

// A class template to provide JS properties.
#define DEFINE_PROPERTY_GETTER(propName) \
    static JSValueRef propName(JSContextRef ctx, JSObjectRef, JSStringRef, JSValueRef*)    { return JSValueMakeNumber(ctx, stat().propName); }
#define DEFINE_PROPERTY_ENTRY(propName) \
    { #propName, propName, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },

template<StatisticGetter stat>
class Statistic {
public:
    static JSStaticValue properties[];
    DEFINE_PROPERTY_GETTER(count)
    DEFINE_PROPERTY_GETTER(size)
    DEFINE_PROPERTY_GETTER(liveSize)
    DEFINE_PROPERTY_GETTER(decodedSize)
    DEFINE_PROPERTY_GETTER(purgeableSize)
    DEFINE_PROPERTY_GETTER(purgedSize)
private:
};

template <StatisticGetter stat>
JSStaticValue Statistic<stat>::properties[] = {
    DEFINE_PROPERTY_ENTRY(count)
    DEFINE_PROPERTY_ENTRY(size)
    DEFINE_PROPERTY_ENTRY(liveSize)
    DEFINE_PROPERTY_ENTRY(decodedSize)
    DEFINE_PROPERTY_ENTRY(purgeableSize)
    DEFINE_PROPERTY_ENTRY(purgedSize)
    { 0, 0, 0, 0 }
};

}

namespace WebKit {

void WebMemoryCacheInfo::registerObject(JSGlobalContextRef ctx)
{
    static JSStringRef protoName = JSStringCreateWithUTF8CString("__proto__");
    static JSStringRef performanceName = JSStringCreateWithUTF8CString("performance");
    static JSStringRef sceMemoryCacheInfoName = JSStringCreateWithUTF8CString("sceMemoryCacheInfo");

    if (s_object)
        JSValueUnprotect(ctx, s_object);
    s_object = create(ctx);
    JSValueProtect(ctx, s_object);

    JSValueRef performanceValue = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), performanceName, 0);
    if (!JSValueIsObject(ctx, performanceValue))
        return;

    JSValueRef protoValue = JSObjectGetProperty(ctx, JSValueToObject(ctx, performanceValue, 0), protoName, 0);
    if (!JSValueIsObject(ctx, protoValue))
        return;

    JSObjectSetProperty(ctx, JSValueToObject(ctx, protoValue, 0), sceMemoryCacheInfoName, s_object,
        kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
}

#define ADD_PROPERTY(klassName, propName) \
    static JSStringRef propName##Str = JSStringCreateWithUTF8CString(#propName); \
    JSClassDefinition klassName = { 0 }; \
    klassName.className = #klassName "CacheInfo"; \
    klassName.staticValues = Statistic<&propName>::properties; \
    JSObjectSetProperty(ctx, sceObj, propName##Str, JSObjectMake(ctx, JSClassCreate(&klassName), 0), \
        kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);

JSObjectRef WebMemoryCacheInfo::create(JSContextRef ctx)
{
    JSClassDefinition jsClassDefinition = {0};
    jsClassDefinition.className = "SceMemoryCacheInfo";
    jsClassDefinition.getProperty = getProperty;

    JSObjectRef sceObj = JSObjectMake(ctx, JSClassCreate(&jsClassDefinition), 0);

    ADD_PROPERTY(Images, images);
    ADD_PROPERTY(CssStyleSheets, cssStyleSheets);
    ADD_PROPERTY(Scripts, scripts);
    ADD_PROPERTY(XslStyleSheets, xslStyleSheets);
    ADD_PROPERTY(Fonts, fonts);

    return sceObj;
}

JSValueRef WebMemoryCacheInfo::getProperty(JSContextRef ctx, JSObjectRef, JSStringRef propertyName, JSValueRef*)
{
    doRefresh(ctx);

    // continue with regular property lookup mechanism (static properties, then parent chain)
    return 0;
}

void WebMemoryCacheInfo::refreshData(JSContextRef ctx)
{
    doRefresh(ctx);
}

void WebMemoryCacheInfo::doRefresh(JSContextRef)
{
    s_statistics = WebCore::memoryCache()->getStatistics();
}

}
